home *** CD-ROM | disk | FTP | other *** search
/ Ham Radio 2000 / Ham Radio 2000.iso / ham2000 / morse / dspmorse / morse.c < prev    next >
C/C++ Source or Header  |  1993-11-28  |  35KB  |  1,255 lines

  1. /////////////////////////////////////////////////////////////////////////////
  2. //    MORSE.C
  3. //        Derived from FFTMORSE.C, Copyright (c) 1992 by François Jalbert.
  4. //        What's new:
  5. //            o    Sound Blaster support is now DMA driven, up to 44000 bytes
  6. //                per second.  Fast machines will lose little or no data.
  7. //            o    Enhanced signal graphing, including color and a max-signal
  8. //                indicator.
  9. //            o   Audio output when the signal meets the mark threshhold.
  10. //                Very handy for fine-tuning reception.
  11. //            o    More FFT-to-tone and tone-to-morse parameters, adjustable
  12. //                while running.
  13. //        The FFT routine is nearly unchanged from François Jalbert's wonderful
  14. //        integer FFT.  Everything else has evolved from ideas in FFTMORSE,
  15. //        also by Mr. Jalbert.  Thank him for me if you see him.
  16. //
  17. //        New bits by Rocco Caputo.
  18. /////////////////////////////////////////////////////////////////////////////
  19.  
  20. #include "morse.h"
  21.  
  22. /////////////////////////////////////////////////////////////////////////////
  23.  
  24. static unsigned int sbSampleRate = SAMPLEMAX;
  25. static unsigned int sbOver = 0;
  26.  
  27. static    char        noise=FALSE;        // audio output toggle
  28. static    char        done=FALSE;            // program termination flag
  29. static    char        autorate=TRUE;        // automatic sample rate negociation
  30. static    char        notTotReset=FALSE;    // flag if it's *NOT* a totFFT reset
  31. static    char        tuneout=FALSE;        // tune-out flag
  32. static    char        pending=FALSE;        // flag for dots/dashes pending
  33.  
  34. static    char        maxlevel[N];        // maximum levels for the max display
  35. static    char        fft[MAXFFT][N];        // scaled FFT buffer for smoothing
  36.  
  37. static    char        tonedetected=FALSE;        // tone-detection indicator
  38. static    char        oldtonedetected=FALSE;    // tone previously detected
  39. static    SCHAR        offset=0;                // threshhold offset
  40. static    ULONG        totMark, totSpace;        // total mark and space buffers
  41. static    UINT        avgMark, avgSpace;        // average mark and space buffers
  42. static    UINT        useMark=4, useSpace=4;    // values to use for mark & space
  43. static    UINT        thisCnt=0;            // current stats
  44. static    UINT        bufMark[MAXBUF];    // mark buffer for averages
  45. static    UINT        bufSpace[MAXBUF];    // space buffer for averages
  46. static    char        cntBuf=32;            // mark/space buffer count
  47. static    char        notMarkReset=FALSE;    // flag to signal mark buffer reset
  48. static    char        notSpaceReset=FALSE;// flag to signal space buffer reset
  49. static    UINT        thrNoise=2;            // runs under this are ignored
  50.  
  51. static    char        waitfactor=16;        // use every Nth byte of input
  52. static    int            dspcount;            // count of bufdsp bytes analyzed
  53. static    int            freqm2,freqm1,        // signal window into FFT results
  54.                     freqp2,freqp1,freq=(N>>1);
  55.  
  56. static    int            outm1,outp1,        // frequency range to tune out
  57.                     out=1;
  58.  
  59. static    int            code=0;                // current morse code being built
  60. static    int            mask=1;                // mask to build morse code with
  61.  
  62. static    UINT        sbStat;                // Becomes 0 when the next block of
  63.                                         // audio is ready for analysis.
  64.  
  65. char far *bufone, far *buftwo;            // generic buffers
  66. char far *bufin, far *bufdsp;            // input and analysys buffers
  67.  
  68. UINT                totFFT[N-1];        // total of the smoothed FFT thang
  69. char                oldFFT[N-1];        // last smoothed FFT thing
  70. char                avgFFT[N-1];        // average FFT over cntFFT iterations
  71. char                cntFFT=1;            // number of FFT thangs to track
  72.  
  73. /////////////////////////////////////////////////////////////////////////////
  74. //
  75. //    Calculate integer FFT on a stream of data.
  76.  
  77. int FFT(char *dsp)
  78. {
  79.                                         // The number of bytes used for the
  80.                                         // transform, returned to the calling
  81.                                         // function so it knows when to go
  82.                                         // and fetch more data.
  83.     int samped;
  84.     int cntFFTm1;
  85.                                         // François Jalbert's FFT variables.
  86.     static int k,l,f[N+N];
  87.     static int level,oldlevel,line,column;
  88.     static int f16p0,f17m1,f17p1,f18m2,f18p2,f19m3,f19p3;
  89.     static int f20m4,f20p4,f21m5,f21p5,f22m6,f22p6,f23m7,f23p7;
  90.     static int f24p8,f25m9,f25p9,f26m10,f26p10,f27m11,f27p11;
  91.     static int f28m12,f28p12,f29m13,f29p13,f30m14,f30p14,f31m15,f31p15;
  92.     static int f25m9Mf23m7,f25m9Pf23m7,f26m10Mf22m6,f26m10Pf22m6;
  93.     static int f27m11Pf21m5,f29m13Mf19m3,f29m13Pf19m3,f27m11Mf21m5;
  94.     static int f30m14Mf18m2,f30m14Pf18m2,f31m15Mf17m1,f31m15Pf17m1;
  95.     static int f24p8Pf16p0,f25p9Mf23p7,f25p9Pf23p7,f26p10Mf22p6,f26p10Pf22p6;
  96.     static int f27p11Mf21p5,f27p11Pf21p5,f28p12Pf20p4,f29p13Mf19p3;
  97.     static int f29p13Pf19p3,f30p14Mf18p2,f30p14Pf18p2,f31p15Mf17p1,f31p15Pf17p1;
  98.     static int f29Mf19Pf27Mf21,f29Pf19Mf27Pf21,f29Mf19Mf27Mf21;
  99.     static int f31Mf17Pf25Mf23,f31Pf17Mf25Pf23,f31Mf17Mf25Mf23;
  100.     static int s4Mf28f20,s4Pf28f20;
  101.     static int s4Mf30f18f26f22,s4Pf30f26f22f18,s4Mf31f29f27f25,s4Pf31f29f27f25;
  102.     static int s8Mf30f18f26f22,s8Pf28f24f20f16,s8Mf24f16,s8Mf28f20,s8f16,s8f24;
  103.     static int s4Mf30f26Ms8Mf28,s4Mf30f26Ps8Mf28;
  104.     static int s4Pf30f22Ms8Mf24,s4Pf30f22Ps8Mf24;
  105.     static int s6Mf31f25Ms2Mf29f27,s6Mf29f27Ps2Mf31f25;
  106.     static int s6Pf29f27Ms2Pf31f25,s6Pf31f25Ps2Pf29f27;
  107.     static int s1Mf25s3f27s5f29s7f31,s1Pf25s3f27s5f29s7f31;
  108.     static int s1Mf27s3f31s5f25s7f29,s1Pf27s3f31s5f25s7f29;
  109.     static int s1Mf29s3f25s5f31s7f27,s1Pf29s3f25s5f31s7f27;
  110.     static int s1Mf31s3f29s5f27s7f25,s1Pf31s3f29s5f27s7f25;
  111.     static int s2Mf26Ps6Mf30,s2Pf26Ms6Pf30,s2Mf30Ms6Mf26,s2Pf30Ps6Pf26;
  112.     static int s4Mf28Ps8f16,s4Mf28Ms8f16,s4Pf28Ps8f24,s4Pf28Ms8f24;
  113.     static int s2Mf26Ps4f28s6f30Ms8f16,s2Mf26Ms4f28s6f30Ps8f16;
  114.     static int s2Pf26Ms4f28s6f30Ps8f24,s2Pf26Ps4f28s6f30Ms8f24;
  115.     static int s2Mf30Ps4f28s6f26Ps8f16,s2Mf30Ms4f28s6f26Ms8f16;
  116.     static int s2Pf30Ps4f28s6f26Ps8f24,s2Pf30Ms4f28s6f26Ms8f24;
  117.                                         // Fill the FFT matrix with samples
  118.                                         // from the input stream.
  119.     f[ 0]=(*(dsp+=waitfactor))-128;
  120.     f[ 1]=(*(dsp+=waitfactor))-128;
  121.     f[ 2]=(*(dsp+=waitfactor))-128;
  122.     f[ 3]=(*(dsp+=waitfactor))-128;
  123.     f[ 4]=(*(dsp+=waitfactor))-128;
  124.     f[ 5]=(*(dsp+=waitfactor))-128;
  125.     f[ 6]=(*(dsp+=waitfactor))-128;
  126.     f[ 7]=(*(dsp+=waitfactor))-128;
  127.     f[ 8]=(*(dsp+=waitfactor))-128;
  128.     f[ 9]=(*(dsp+=waitfactor))-128;
  129.     f[10]=(*(dsp+=waitfactor))-128;
  130.     f[11]=(*(dsp+=waitfactor))-128;
  131.     f[12]=(*(dsp+=waitfactor))-128;
  132.     f[13]=(*(dsp+=waitfactor))-128;
  133.     f[14]=(*(dsp+=waitfactor))-128;
  134.     f[15]=(*(dsp+=waitfactor))-128;
  135.     f[16]=(*(dsp+=waitfactor))-128;
  136.     f[17]=(*(dsp+=waitfactor))-128;
  137.     f[18]=(*(dsp+=waitfactor))-128;
  138.     f[19]=(*(dsp+=waitfactor))-128;
  139.     f[20]=(*(dsp+=waitfactor))-128;
  140.     f[21]=(*(dsp+=waitfactor))-128;
  141.     f[22]=(*(dsp+=waitfactor))-128;
  142.     f[23]=(*(dsp+=waitfactor))-128;
  143.     f[24]=(*(dsp+=waitfactor))-128;
  144.     f[25]=(*(dsp+=waitfactor))-128;
  145.     f[26]=(*(dsp+=waitfactor))-128;
  146.     f[27]=(*(dsp+=waitfactor))-128;
  147.     f[28]=(*(dsp+=waitfactor))-128;
  148.     f[29]=(*(dsp+=waitfactor))-128;
  149.     f[30]=(*(dsp+=waitfactor))-128;
  150.     f[31]=(*(dsp+=waitfactor))-128;
  151.  
  152.     samped += waitfactor<<5;
  153.                                         // Calculate the integer FFT values
  154.                                         // for 16 frequency breaks.
  155.     f17m1 =f[17]-f[1];  f17p1 =f[17]+f[1];
  156.     f18m2 =f[18]-f[2];  f18p2 =f[18]+f[2];
  157.     f19m3 =f[19]-f[3];  f19p3 =f[19]+f[3];
  158.     f20m4 =f[20]-f[4];  f20p4 =f[20]+f[4];
  159.     f21m5 =f[21]-f[5];  f21p5 =f[21]+f[5];
  160.     f22m6 =f[22]-f[6];  f22p6 =f[22]+f[6];
  161.     f23m7 =f[23]-f[7];  f23p7 =f[23]+f[7];
  162.     f16p0 =f[16]+f[0];  f24p8 =f[24]+f[8];
  163.     f25m9 =f[25]-f[9];  f25p9 =f[25]+f[9];
  164.     f26m10=f[26]-f[10]; f26p10=f[26]+f[10];
  165.     f27m11=f[27]-f[11]; f27p11=f[27]+f[11];
  166.     f28m12=f[28]-f[12]; f28p12=f[28]+f[12];
  167.     f29m13=f[29]-f[13]; f29p13=f[29]+f[13];
  168.     f30m14=f[30]-f[14]; f30p14=f[30]+f[14];
  169.     f31m15=f[31]-f[15]; f31p15=f[31]+f[15];
  170.     f25m9Mf23m7 =f25m9 -f23m7; f25m9Pf23m7 =f25m9 +f23m7;
  171.     f26m10Mf22m6=f26m10-f22m6; f26m10Pf22m6=f26m10+f22m6;
  172.     f27m11Mf21m5=f27m11-f21m5; f27m11Pf21m5=f27m11+f21m5;
  173.     f29m13Mf19m3=f29m13-f19m3; f29m13Pf19m3=f29m13+f19m3;
  174.     f30m14Mf18m2=f30m14-f18m2; f30m14Pf18m2=f30m14+f18m2;
  175.     f31m15Mf17m1=f31m15-f17m1; f31m15Pf17m1=f31m15+f17m1;
  176.     f25p9Mf23p7 =f25p9 -f23p7; f25p9Pf23p7 =f25p9 +f23p7;
  177.     f26p10Mf22p6=f26p10-f22p6; f26p10Pf22p6=f26p10+f22p6;
  178.     f27p11Mf21p5=f27p11-f21p5; f27p11Pf21p5=f27p11+f21p5;
  179.     f24p8Pf16p0 =f24p8 +f16p0; f28p12Pf20p4=f28p12+f20p4;
  180.     f29p13Mf19p3=f29p13-f19p3; f29p13Pf19p3=f29p13+f19p3;
  181.     f30p14Mf18p2=f30p14-f18p2; f30p14Pf18p2=f30p14+f18p2;
  182.     f31p15Mf17p1=f31p15-f17p1; f31p15Pf17p1=f31p15+f17p1;
  183.     f29Mf19Pf27Mf21=f29p13Mf19p3+f27p11Mf21p5;
  184.     f29Pf19Mf27Pf21=f29p13Pf19p3-f27p11Pf21p5;
  185.     f29Mf19Mf27Mf21=f29p13Mf19p3-f27p11Mf21p5;
  186.     f31Mf17Pf25Mf23=f31p15Mf17p1+f25p9Mf23p7;
  187.     f31Pf17Mf25Pf23=f31p15Pf17p1-f25p9Pf23p7;
  188.     f31Mf17Mf25Mf23=f31p15Mf17p1-f25p9Mf23p7;
  189.     s4Mf30f18f26f22=S4*(f30p14Mf18p2+f26p10Mf22p6);
  190.     s4Pf30f26f22f18=S4*(f30p14Pf18p2-f26p10Pf22p6);
  191.     s4Mf31f29f27f25=S4*(f31Mf17Mf25Mf23+f29Mf19Mf27Mf21);
  192.     s4Pf31f29f27f25=S4*(f31p15Pf17p1+f25p9Pf23p7-f29p13Pf19p3-f27p11Pf21p5);
  193.     s8Mf30f18f26f22=S8*(f30p14Mf18p2-f26p10Mf22p6);
  194.     s8Pf28f24f20f16=S8*(f28p12Pf20p4-f24p8Pf16p0);
  195.     s8Mf24f16=S8*(f24p8-f16p0);
  196.     s8Mf28f20=S8*(f28p12-f20p4);
  197.     s4Mf30f26Ms8Mf28=s4Mf30f18f26f22-s8Mf28f20;
  198.     s4Mf30f26Ps8Mf28=s4Mf30f18f26f22+s8Mf28f20;
  199.     s4Pf30f22Ms8Mf24=s4Pf30f26f22f18-s8Mf24f16;
  200.     s4Pf30f22Ps8Mf24=s4Pf30f26f22f18+s8Mf24f16;
  201.     s6Mf31f25Ms2Mf29f27=S6*f31Mf17Pf25Mf23-S2*f29Mf19Pf27Mf21;
  202.     s6Mf29f27Ps2Mf31f25=S6*f29Mf19Pf27Mf21+S2*f31Mf17Pf25Mf23;
  203.     s6Pf29f27Ms2Pf31f25=S6*f29Pf19Mf27Pf21-S2*f31Pf17Mf25Pf23;
  204.     s6Pf31f25Ps2Pf29f27=S6*f31Pf17Mf25Pf23+S2*f29Pf19Mf27Pf21;
  205.     s1Mf25s3f27s5f29s7f31=S1*f25m9Mf23m7+S3*f27m11Mf21m5+S5*f29m13Mf19m3+S7*f31m15Mf17m1;
  206.     s1Pf25s3f27s5f29s7f31=S1*f25m9Pf23m7-S3*f27m11Pf21m5+S5*f29m13Pf19m3-S7*f31m15Pf17m1;
  207.     s1Mf27s3f31s5f25s7f29=S1*f27m11Mf21m5+S3*f31m15Mf17m1+S5*f25m9Mf23m7-S7*f29m13Mf19m3;
  208.     s1Pf27s3f31s5f25s7f29=S1*f27m11Pf21m5+S3*f31m15Pf17m1-S5*f25m9Pf23m7+S7*f29m13Pf19m3;
  209.     s1Mf29s3f25s5f31s7f27=S1*f29m13Mf19m3+S3*f25m9Mf23m7-S5*f31m15Mf17m1+S7*f27m11Mf21m5;
  210.     s1Pf29s3f25s5f31s7f27=S1*f29m13Pf19m3+S3*f25m9Pf23m7+S5*f31m15Pf17m1-S7*f27m11Pf21m5;
  211.     s1Mf31s3f29s5f27s7f25=S1*f31m15Mf17m1-S3*f29m13Mf19m3+S5*f27m11Mf21m5-S7*f25m9Mf23m7;
  212.     s1Pf31s3f29s5f27s7f25=S1*f31m15Pf17m1+S3*f29m13Pf19m3+S5*f27m11Pf21m5+S7*f25m9Pf23m7;
  213.     s2Mf26Ps6Mf30=S2*f26m10Mf22m6+S6*f30m14Mf18m2;;
  214.     s2Pf26Ms6Pf30=S2*f26m10Pf22m6-S6*f30m14Pf18m2;
  215.     s2Mf30Ms6Mf26=S2*f30m14Mf18m2-S6*f26m10Mf22m6;
  216.     s2Pf30Ps6Pf26=S2*f30m14Pf18m2+S6*f26m10Pf22m6;
  217.     s4Mf28f20=S4*(f28m12-f20m4); s4Pf28f20=S4*(f28m12+f20m4);
  218.     s8f16=S8*(f[16]-f[0]); s8f24=S8*(f[24]-f[8]);
  219.     s4Mf28Ps8f16=s4Mf28f20+s8f16;
  220.     s4Mf28Ms8f16=s4Mf28f20-s8f16;
  221.     s4Pf28Ps8f24=s4Pf28f20+s8f24;
  222.     s4Pf28Ms8f24=s4Pf28f20-s8f24;
  223.     s2Mf26Ps4f28s6f30Ms8f16=s2Mf26Ps6Mf30+s4Mf28Ms8f16;
  224.     s2Mf26Ms4f28s6f30Ps8f16=s2Mf26Ps6Mf30-s4Mf28Ms8f16;
  225.     s2Pf26Ms4f28s6f30Ps8f24=s2Pf26Ms6Pf30-s4Pf28Ms8f24;
  226.     s2Pf26Ps4f28s6f30Ms8f24=s2Pf26Ms6Pf30+s4Pf28Ms8f24;
  227.     s2Mf30Ps4f28s6f26Ps8f16=s2Mf30Ms6Mf26+s4Mf28Ps8f16;
  228.     s2Mf30Ms4f28s6f26Ms8f16=s2Mf30Ms6Mf26-s4Mf28Ps8f16;
  229.     s2Pf30Ps4f28s6f26Ps8f24=s2Pf30Ps6Pf26+s4Pf28Ps8f24;
  230.     s2Pf30Ms4f28s6f26Ms8f24=s2Pf30Ps6Pf26-s4Pf28Ps8f24;
  231.                                         // Copy the current average FFT
  232.                                         // values to a temporary buffer for
  233.                                         // erasing the display.
  234.     memcpy(oldFFT, avgFFT, sizeof(oldFFT));
  235.                                         // Subtract the oldest FFT result
  236.                                         // from the average total.
  237.     if (notTotReset)
  238.     {
  239.         totFFT[ 0] -= fft[0][ 0];
  240.         totFFT[ 1] -= fft[0][ 1];
  241.         totFFT[ 2] -= fft[0][ 2];
  242.         totFFT[ 3] -= fft[0][ 3];
  243.         totFFT[ 4] -= fft[0][ 4];
  244.         totFFT[ 5] -= fft[0][ 5];
  245.         totFFT[ 6] -= fft[0][ 6];
  246.         totFFT[ 7] -= fft[0][ 7];
  247.         totFFT[ 8] -= fft[0][ 8];
  248.         totFFT[ 9] -= fft[0][ 9];
  249.         totFFT[10] -= fft[0][10];
  250.         totFFT[11] -= fft[0][11];
  251.         totFFT[12] -= fft[0][12];
  252.         totFFT[13] -= fft[0][13];
  253.         totFFT[14] -= fft[0][14];
  254.     }
  255.     notTotReset = TRUE;
  256.                                         // Shift the FFT buffer left one
  257.                                         // graph, erasing the oldest FFT and
  258.                                         // making room for the new one.
  259.     memmove(&(fft[0][0]), &(fft[1][0]), N*(cntFFT-1));
  260.                                         // Calculate cntFFT minus 1 for speed
  261.     cntFFTm1 = cntFFT - 1;
  262.                                         // Fill in the resulting FFT array
  263.                                         // while adding the new values to the
  264.                                         // FFT total for "cntFFT" iterations.
  265.                                         // Also calculate the average FFT at
  266.                                         // this time.
  267.     avgFFT[ 0] =
  268.         (
  269.         totFFT[ 0] +=
  270.             (
  271.                 fft[cntFFTm1][ 0] =
  272.                 (abs(s1Mf25s3f27s5f29s7f31+s2Mf26Ps4f28s6f30Ms8f16)>>SCALE)+
  273.                 (abs(s1Pf31s3f29s5f27s7f25+s2Pf30Ps4f28s6f26Ps8f24)>>SCALE)
  274.             )
  275.         ) / cntFFT;
  276.     avgFFT[ 1] =
  277.         (
  278.         totFFT[ 1] +=
  279.             (
  280.                 fft[cntFFTm1][ 1] =
  281.                 (abs(s6Pf31f25Ps2Pf29f27+s4Pf30f22Ms8Mf24)>>SCALE)+
  282.                 (abs(s6Mf29f27Ps2Mf31f25+s4Mf30f26Ps8Mf28)>>SCALE)
  283.             )
  284.         ) / cntFFT;
  285.     avgFFT[ 2] =
  286.         (
  287.         totFFT[ 2] +=
  288.             (
  289.                 fft[cntFFTm1][ 2] =
  290.                 (abs(s2Mf30Ms4f28s6f26Ms8f16-s1Mf29s3f25s5f31s7f27)>>SCALE)+
  291.                 (abs(s2Pf26Ms4f28s6f30Ps8f24-s1Pf27s3f31s5f25s7f29)>>SCALE)
  292.             )
  293.         ) / cntFFT;
  294.     avgFFT[ 3] =
  295.         (
  296.         totFFT[ 3] +=
  297.             (
  298.                 fft[cntFFTm1][ 3] =
  299.                 (abs(s4Pf31f29f27f25-s8Pf28f24f20f16)>>SCALE)+
  300.                 (abs(s4Mf31f29f27f25+s8Mf30f18f26f22)>>SCALE)
  301.             )
  302.         ) / cntFFT;
  303.     avgFFT[ 4] =
  304.         (
  305.         totFFT[ 4] +=
  306.             (
  307.                 fft[cntFFTm1][ 4] =
  308.                 (abs(s1Mf27s3f31s5f25s7f29-s2Mf30Ps4f28s6f26Ps8f16)>>SCALE)+
  309.                 (abs(s2Pf26Ps4f28s6f30Ms8f24-s1Pf29s3f25s5f31s7f27)>>SCALE)
  310.             )
  311.         ) / cntFFT;
  312.     avgFFT[ 5] =
  313.         (
  314.         totFFT[ 5] +=
  315.             (
  316.                 fft[cntFFTm1][ 5] =
  317.                 (abs(s6Pf29f27Ms2Pf31f25+s4Pf30f22Ps8Mf24)>>SCALE)+
  318.                 (abs(s6Mf31f25Ms2Mf29f27+s4Mf30f26Ms8Mf28)>>SCALE)
  319.             )
  320.         ) / cntFFT;
  321.     avgFFT[ 6] =
  322.         (
  323.         totFFT[ 6] +=
  324.             (
  325.                 fft[cntFFTm1][ 6] =
  326.                 (abs(s1Mf31s3f29s5f27s7f25-s2Mf26Ms4f28s6f30Ps8f16)>>SCALE)+
  327.                 (abs(s1Pf25s3f27s5f29s7f31-s2Pf30Ms4f28s6f26Ms8f24)>>SCALE)
  328.             )
  329.         ) / cntFFT;
  330.     avgFFT[ 7] =
  331.         (
  332.         totFFT[ 7] +=
  333.             (
  334.                 fft[cntFFTm1][ 7] =
  335.                 (abs(S8*(f30p14Pf18p2+f26p10Pf22p6-f28p12Pf20p4-f24p8Pf16p0))>>SCALE)+
  336.                 (abs(S8*(f31Mf17Mf25Mf23-f29Mf19Mf27Mf21))>>SCALE)
  337.             )
  338.         ) / cntFFT;
  339.     avgFFT[ 8] =
  340.         (
  341.         totFFT[ 8] +=
  342.             (
  343.                 fft[cntFFTm1][ 8] =
  344.                 (abs(s1Mf31s3f29s5f27s7f25+s2Mf26Ms4f28s6f30Ps8f16)>>SCALE)+
  345.                 (abs(s1Pf25s3f27s5f29s7f31+s2Pf30Ms4f28s6f26Ms8f24)>>SCALE)
  346.             )
  347.         ) / cntFFT;
  348.     avgFFT[ 9] =
  349.         (
  350.         totFFT[ 9] +=
  351.             (
  352.                 fft[cntFFTm1][ 9] =
  353.                 (abs(s6Pf29f27Ms2Pf31f25-s4Pf30f22Ps8Mf24)>>SCALE)+
  354.                 (abs(s4Mf30f26Ms8Mf28-s6Mf31f25Ms2Mf29f27)>>SCALE)
  355.             )
  356.         ) / cntFFT;
  357.     avgFFT[10] =
  358.         (
  359.         totFFT[10] +=
  360.             (
  361.                 fft[cntFFTm1][10] =
  362.                 (abs(s1Mf27s3f31s5f25s7f29+s2Mf30Ps4f28s6f26Ps8f16)>>SCALE)+
  363.                 (abs(s1Pf29s3f25s5f31s7f27+s2Pf26Ps4f28s6f30Ms8f24)>>SCALE)
  364.             )
  365.         ) / cntFFT;
  366.     avgFFT[11] =
  367.         (
  368.         totFFT[11] +=
  369.             (
  370.                 fft[cntFFTm1][11] =
  371.                 (abs(s8Pf28f24f20f16+s4Pf31f29f27f25)>>SCALE)+
  372.                 (abs(s8Mf30f18f26f22-s4Mf31f29f27f25)>>SCALE)
  373.             )
  374.         ) / cntFFT;
  375.     avgFFT[12] =
  376.         (
  377.         totFFT[12] +=
  378.             (
  379.                 fft[cntFFTm1][12] =
  380.                 (abs(s1Mf29s3f25s5f31s7f27+s2Mf30Ms4f28s6f26Ms8f16)>>SCALE)+
  381.                 (abs(s1Pf27s3f31s5f25s7f29+s2Pf26Ms4f28s6f30Ps8f24)>>SCALE)
  382.             )
  383.         ) / cntFFT;
  384.     avgFFT[13] =
  385.         (
  386.         totFFT[13] +=
  387.             (
  388.                 fft[cntFFTm1][13] =
  389.                 (abs(s4Pf30f22Ms8Mf24-s6Pf31f25Ps2Pf29f27)>>SCALE)+
  390.                 (abs(s4Mf30f26Ps8Mf28-s6Mf29f27Ps2Mf31f25)>>SCALE)
  391.             )
  392.         ) / cntFFT;
  393.     avgFFT[14] =
  394.         (
  395.         totFFT[14] +=
  396.             (
  397.                 fft[cntFFTm1][14] =
  398.                 (abs(s2Mf26Ps4f28s6f30Ms8f16-s1Mf25s3f27s5f29s7f31)>>SCALE)+
  399.                 (abs(s2Pf30Ps4f28s6f26Ps8f24-s1Pf31s3f29s5f27s7f25)>>SCALE)
  400.             )
  401.         ) / cntFFT;
  402. /*
  403.                                         // Provide a filter to drop out low
  404.                                         // signals.
  405.     if (avgFFT[ 0]<offset) avgFFT[ 0] = 0;
  406.     if (avgFFT[ 1]<offset) avgFFT[ 1] = 0;
  407.     if (avgFFT[ 2]<offset) avgFFT[ 2] = 0;
  408.     if (avgFFT[ 3]<offset) avgFFT[ 3] = 0;
  409.     if (avgFFT[ 4]<offset) avgFFT[ 4] = 0;
  410.     if (avgFFT[ 5]<offset) avgFFT[ 5] = 0;
  411.     if (avgFFT[ 6]<offset) avgFFT[ 6] = 0;
  412.     if (avgFFT[ 7]<offset) avgFFT[ 7] = 0;
  413.     if (avgFFT[ 8]<offset) avgFFT[ 8] = 0;
  414.     if (avgFFT[ 9]<offset) avgFFT[ 9] = 0;
  415.     if (avgFFT[10]<offset) avgFFT[10] = 0;
  416.     if (avgFFT[11]<offset) avgFFT[11] = 0;
  417.     if (avgFFT[12]<offset) avgFFT[12] = 0;
  418.     if (avgFFT[13]<offset) avgFFT[13] = 0;
  419.     if (avgFFT[14]<offset) avgFFT[14] = 0;
  420. */
  421.                                         // Display the smoothed FFT to the
  422.                                         // on-screen audio spectrum analyzer.
  423.     for (l=0,line=0; l<(N-1); l++,line+=160)
  424.     {
  425.         level = avgFFT[l];
  426.         oldlevel = oldFFT[l];
  427.                                         // Display the signal strength for
  428.                                         // this frequency.
  429.         for (k=1,column=2; k<=level; k++,column+=2)
  430.         {
  431.             pokeb(VIDSEG,80+line+column,0xFE);
  432.         }
  433.                                         // Erase any left-overs for the
  434.                                         // previous sample.
  435.         for (k=level+1; k<=oldlevel; k++,column+=2)
  436.         {
  437.             pokeb(VIDSEG,80+line+column,0xFA);
  438.         }
  439.                                         // Display the max-strength level for
  440.                                         // this frequency, if it's changed.
  441.         if ((level)&&((level+1) >= maxlevel[l]))
  442.         {
  443.             maxlevel[l] = level+1;
  444.             pokeb(VIDSEG,80+line+(maxlevel[l]<<1),0x07);
  445.         }
  446.     }
  447.                                         // Tell the calling function how many
  448.                                         // samples this function has used.
  449.     return(samped);
  450. }
  451.  
  452. /////////////////////////////////////////////////////////////////////////////
  453. //
  454. //    Convert dits and dahs to text.  The way it's done wasn't immediately
  455. //    apparent to me, so I'll explain:  Morse dots are added as 1 bits, right
  456. //    to left in the code value.  Eventually, when a space is detected, the
  457. //    value in code is the reverse of the morse letter.  Since it's a single
  458. //    value, it's easily decoded into text.  Very smart!
  459. //    To do:  Convert the switch statement to a look-up table.
  460.  
  461. //
  462. //    Add a "dot" to the morse code being built.
  463. //
  464. void doDot()
  465. {
  466.     morsePutC(0xF9);
  467.     code += mask;
  468.     mask <<= 1;
  469.     pending = TRUE;
  470. }
  471.  
  472. //
  473. //    Add a "dash" to the morse code being built.
  474. //
  475. void doDash()
  476. {
  477.     morsePutC('-');
  478.     mask <<= 1;
  479.     pending = TRUE;
  480. }
  481.  
  482. //
  483. //    Space is detected between morse codes, so translate what was built into
  484. //    a letter and display it.
  485. //
  486. void doSpace()
  487. {
  488.     char c;
  489.  
  490.     if (pending)
  491.     {
  492.         code += mask;
  493.  
  494.         switch (code)
  495.         {
  496.             case 2:        c='T'; break;    // -
  497.             case 3:        c='E'; break;    // -
  498.             case 4:        c='M'; break;    // --
  499.             case 5:        c='A'; break;    // ∙-
  500.             case 6:        c='N'; break;    // -∙
  501.             case 7:        c='I'; break;    // ∙∙
  502.             case 8:        c='O'; break;    // ---
  503.             case 9:        c='W'; break;    // ∙--
  504.             case 10:    c='K'; break;    // -∙-
  505.             case 11:    c='U'; break;    // ∙∙-
  506.             case 12:    c='G'; break;    // --∙
  507.             case 13:    c='R'; break;    // ∙-∙
  508.             case 14:    c='D'; break;    // -∙∙
  509.             case 15:    c='S'; break;    // ∙∙∙
  510.             case 17:    c='J'; break;    // ∙---
  511.             case 18:    c='Y'; break;    // -∙--
  512.             case 19:    c='Ü'; break;    // ∙∙--
  513.             case 20:    c='Q'; break;    // --∙-
  514.             case 21:    c='Ä'; break;    // ∙-∙-
  515.             case 22:    c='X'; break;    // -∙∙-
  516.             case 23:    c='V'; break;    // ∙∙∙-
  517.             case 24:    c='Ö'; break;    // ---∙
  518.             case 25:    c='P'; break;    // ∙--∙
  519.             case 26:    c='C'; break;    // -∙-∙
  520.             case 27:    c='F'; break;    // ∙∙-∙
  521.             case 28:    c='Z'; break;    // --∙∙
  522.             case 29:    c='L'; break;    // ∙-∙∙
  523.             case 30:    c='B'; break;    // -∙∙∙
  524.             case 31:    c='H'; break;    // ∙∙∙∙
  525.             case 32:    c='0'; break;    // -----
  526.             case 33:    c='1'; break;    // ∙----
  527.             case 35:    c='2'; break;    // ∙∙---
  528.             case 36:    c='Ñ'; break;    // --∙--
  529.             case 39:    c='3'; break;    // ∙∙∙--
  530.             case 41:    c='Å'; break;    // ∙--∙-
  531.             case 47:    c='4'; break;    // ∙∙∙∙-
  532.             case 48:    c='9'; break;    // ----∙
  533.             case 54:    c='/'; break;    //
  534.             case 56:    c='8'; break;    // ---∙∙
  535.             case 59:    c='É'; break;    // ∙∙-∙∙
  536.             case 60:    c='7'; break;    // --∙∙∙
  537.             case 62:    c='6'; break;    // -∙∙∙∙
  538.             case 63:    c='5'; break;    // ∙∙∙∙∙
  539.             case 76:    c=','; break;    //
  540.             case 82:    c='|'; break;    //
  541.             case 85:    c='.'; break;    //
  542.             case 94:    c='-'; break;    //
  543.             case 106:    c=';'; break;    //
  544.             case 115:    c='?'; break;    //
  545.             case 120:    c=':'; break;    //
  546.             default:
  547.                 c=0xFE;
  548.                 morsePutC(0xFE);
  549.                 break;
  550.         }
  551.  
  552.         morsePutC(0x20);
  553.         textPutC(c);
  554.         if (thisCnt >= (useSpace*3))
  555.         {
  556.             textPutC(0x20);
  557.         }
  558.  
  559.         code=0;
  560.         mask=1;
  561.     }
  562.     pending=FALSE;
  563. }
  564.  
  565. /////////////////////////////////////////////////////////////////////////////
  566. //
  567. //    Determine if a mark is present in the FFT window.
  568. //    To do:  Allow for 1-row, 3-row and 5-row windows.
  569.  
  570. void FFT2tone(void)
  571. {
  572.     static int target=0;
  573.     static int foctot=0, focavg=0;
  574.     static int remavg=0;
  575.     static int alltot=0, allavg=0;
  576.     static int oldremavg=0, oldfocavg=0, oldallavg=0;
  577.  
  578.     oldfocavg = focavg;
  579.     focavg=    (
  580.                 (
  581.                     foctot=    (
  582.                                 avgFFT[freqm2]+
  583.                                 avgFFT[freqm1]+
  584.                                 avgFFT[freq]+
  585.                                 avgFFT[freqp1]+
  586.                                 avgFFT[freqp2]
  587.                             )
  588.                 ) << 1
  589.             ) / 5;
  590.  
  591.     oldallavg = allavg;
  592.     alltot=    (
  593.                 avgFFT[ 0]+
  594.                 avgFFT[ 1]+
  595.                 avgFFT[ 2]+
  596.                 avgFFT[ 3]+
  597.                 avgFFT[ 4]+
  598.                 avgFFT[ 5]+
  599.                 avgFFT[ 6]+
  600.                 avgFFT[ 7]+
  601.                 avgFFT[ 8]+
  602.                 avgFFT[ 9]+
  603.                 avgFFT[10]+
  604.                 avgFFT[11]+
  605.                 avgFFT[12]+
  606.                 avgFFT[13]+
  607.                 avgFFT[14]
  608.             );
  609.     if (tuneout)
  610.     {
  611.         alltot -=    (
  612.                         avgFFT[outm1]+
  613.                         avgFFT[out]+
  614.                         avgFFT[outp1]
  615.                     );
  616.     }
  617.  
  618.     allavg = (alltot<<1) / (N-1);
  619.  
  620.     oldremavg = remavg;
  621.     remavg =    ((alltot-foctot)<<1) / ((N-1)-5);
  622.                                         // Erase old averages display.
  623.     pokeb(VIDSEG, 2482+(oldfocavg<<1), 0x20);
  624.     pokeb(VIDSEG, 2482+(oldallavg<<1), 0x20);
  625.     pokeb(VIDSEG, 2482+(oldremavg<<1), 0x20);
  626.     pokeb(VIDSEG, 2482+(target<<1), 0x20);
  627.  
  628.     pokeb(VIDSEG, 2482+(allavg<<1), 0x1E);
  629.     pokeb(VIDSEG, 2482+(remavg<<1), 0x1F);
  630.     pokeb(VIDSEG, 2482+(focavg<<1), 0x18);
  631.                                         // Calculate target signal level.
  632.                                         // If the focus average is above this
  633.                                         // target, a tone is considered to be
  634.                                         // present.
  635.     target = ((allavg+remavg)>>1)+offset;
  636.     pokeb(VIDSEG, 2482+(target<<1), 0xB3);
  637.                                         // Determine if a tone is present.
  638.                                         // Adds optional audio feedback, too.
  639.     oldtonedetected = tonedetected;
  640.     if ((focavg) > (target))
  641.     {
  642.         tonedetected=TRUE;
  643.         if (noise)
  644.         {
  645.             sound(880);
  646.         }
  647.         tonePutC(0xFE);
  648.     }
  649.     else
  650.     {
  651.         tonedetected=FALSE;
  652.         nosound();
  653.         tonePutC(0x20);
  654.     }
  655.                                         // Edge-detect mark-to-space and
  656.                                         // space-to-mark transitions.
  657.     if (oldtonedetected != tonedetected)
  658.     {
  659.         if (thisCnt>=thrNoise)
  660.         {
  661.             if (tonedetected)
  662.             {
  663.                 if ((totSpace+thisCnt)>totSpace)
  664.                 {
  665.                     if (notSpaceReset)
  666.                     {
  667.                         totSpace -= bufSpace[0];
  668.                     }
  669.                     notSpaceReset = TRUE;
  670.                     memmove(bufSpace, bufSpace+1, (cntBuf-1)*sizeof(bufSpace[0]));
  671.                     avgSpace = (totSpace += (bufSpace[cntBuf-1] = thisCnt))/cntBuf;
  672.  
  673.                     if (thisCnt >= useSpace)
  674.                     {
  675.                         doSpace();
  676.                     }
  677.                 }
  678.             }
  679.             else
  680.             {
  681.                 if ((totMark+thisCnt)>totMark)
  682.                 {
  683.                     if (notMarkReset)
  684.                     {
  685.                         totMark -= bufMark[0];
  686.                     }
  687.                     notMarkReset = TRUE;
  688.                     memmove(bufMark, bufMark+1, (cntBuf-1)*sizeof(bufSpace[0]));
  689.                     avgMark = (totMark += (bufMark[cntBuf-1] = thisCnt))/cntBuf;
  690.  
  691.                     if (thisCnt >= useMark)
  692.                     {
  693.                         doDash();
  694.                     }
  695.                     else
  696.                     {
  697.                         doDot();
  698.                     }
  699.                 }
  700.             }
  701.             thisCnt=0;
  702.         }
  703.     }
  704.     else
  705.     {
  706.         if (!tonedetected)
  707.         {
  708.             if (pending && (thisCnt>(useSpace*3)))
  709.             {
  710.                 doSpace();
  711.                 notSpaceReset = TRUE;
  712.                 memmove(bufSpace, bufSpace+1, (cntBuf-1)*sizeof(bufSpace[0]));
  713.                 avgSpace = (totSpace += (bufSpace[cntBuf-1] = thisCnt))/cntBuf;
  714.                 thisCnt=0;
  715.             }
  716.         }
  717.     }
  718.     thisCnt+=1;
  719. }
  720.  
  721. /////////////////////////////////////////////////////////////////////////////
  722.  
  723. void updateCursors(char onoff)
  724. {
  725.     char c1, c2;
  726.     int c, col;
  727.                                         // Determine the cursor characters.
  728.     if (onoff)
  729.     {
  730.         c1 = '<';
  731.         c2 = '>';
  732.     }
  733.     else
  734.     {
  735.         c1 = c2 = ' ';
  736.     }
  737.                                         // Calculate the focus cursor range.
  738.     freqm2 = freq - 2;
  739.     freqm1 = freq - 1;
  740.     freqp1 = freq + 1;
  741.     freqp2 = freq + 2;
  742.                                         // Calculate the tuned-out cursor.
  743.     outm1 = out - 1;
  744.     outp1 = out + 1;
  745.                                         // Display the tuned-out cursor.
  746.     pokeb(VIDSEG,(74)+(160*outm1), c1);
  747.     pokeb(VIDSEG,(74)+(160*out), c1);
  748.     pokeb(VIDSEG,(74)+(160*outp1), c1);
  749.                                         // Tune out that graph portion.
  750.     for (c=41; c<GRAPHTOP; c++)
  751.     {
  752.         if (tuneout && onoff)
  753.         {
  754.             col=8;
  755.         }
  756.         else
  757.         {
  758.             if        (c<BLU)    col=9;
  759.             else if (c<GRN)    col=10;
  760.             else if (c<YEL)    col=14;
  761.             else            col=12;
  762.         }
  763.         pokeb(VIDSEG,((outm1)*160)+(c<<1)+1,col);
  764.         pokeb(VIDSEG,((out)*160)+(c<<1)+1,col);
  765.         pokeb(VIDSEG,((outp1)*160)+(c<<1)+1,col);
  766.     }
  767.                                         // Display the frequency cursor.
  768.     pokeb(VIDSEG,(76)+(160*freqm2),c2);
  769.     pokeb(VIDSEG,(76)+(160*freqm1),c2);
  770.     pokeb(VIDSEG,(76)+(160*freq),c2);
  771.     pokeb(VIDSEG,(76)+(160*freqp1),c2);
  772.     pokeb(VIDSEG,(76)+(160*freqp2),c2);
  773.                                         // Focus on the graph bit.
  774.     for (c=41; c<GRAPHTOP; c++)
  775.     {
  776.         if        (c<BLU)    col=9;
  777.         else if (c<GRN)    col=10;
  778.         else if (c<YEL)    col=14;
  779.         else            col=12;
  780.         if (onoff)
  781.         {
  782.             col |= 0x68;
  783.         }
  784.         pokeb(VIDSEG,((freqm2)*160)+(c<<1)+1,col);
  785.         pokeb(VIDSEG,((freqm1)*160)+(c<<1)+1,col);
  786.         pokeb(VIDSEG,((freq)*160)+(c<<1)+1,col);
  787.         pokeb(VIDSEG,((freqp1)*160)+(c<<1)+1,col);
  788.         pokeb(VIDSEG,((freqp2)*160)+(c<<1)+1,col);
  789.     }
  790. }
  791.  
  792. /////////////////////////////////////////////////////////////////////////////
  793.  
  794. void clearGraph(void)
  795. {
  796.     int l, l160, c;
  797.     char col;
  798.                                         // Clear the audio spectrum analyzer
  799.                                         // on the screen.
  800.     for (l=0,l160=0; l<(N-1); l++,l160+=160)
  801.     {
  802.         for (c=41; c<GRAPHTOP; c++)
  803.         {
  804.             if        (c<BLU)    col=9;        // 9
  805.             else if (c<GRN)    col=10;        // 10
  806.             else if (c<YEL)    col=14;        // 14
  807.             else            col=12;        // 12
  808.             pokeb(VIDSEG,l160+(c<<1),0xFA);
  809.             pokeb(VIDSEG,l160+(c<<1)+1,col);
  810.         }
  811.     }
  812.                                         // Squonk the old and average FFT
  813.                                         // values, since these will change.
  814.     memset(oldFFT, 0, sizeof(oldFFT));
  815.     memset(totFFT, 0, sizeof(totFFT));
  816.     memset(fft, 0, sizeof(fft));
  817.     memset(maxlevel, 0, sizeof(maxlevel));
  818.                                         // Reset the total FFT buffer too.
  819.     notTotReset = FALSE;
  820.  
  821. }
  822.  
  823.  
  824. #include <bios.h>
  825.  
  826. #define UPKEY     0x4800
  827. #define SUPKEY    0x4838
  828. #define SFIVEKEY  0x4C35
  829. #define DOWNKEY   0x5000
  830. #define SDOWNKEY  0x5032
  831. #define ESCKEY    0x011B
  832. #define PLUSKEY   0x4E2B
  833. #define PLUSKEY2  0x0D2B
  834. #define MINUSKEY  0x4A2D
  835. #define MINUSKEY2 0x0C2D
  836. #define SPACEKEY  0x3920
  837. #define STARKEY   0x372A
  838. #define STARKEY2  0x092A
  839. #define LEFTKEY        0x4B00
  840. #define RIGHTKEY    0x4D00
  841.  
  842. /*
  843.       3 4         5         6         7
  844.       89-123456789-123456789-123456789-123456789
  845. 17    1|(QW) Freq: 12345 *  ---- Overruns: 33333
  846. 18    2|(AS) Skip: 123        (ER) Smooth  : 333
  847. 19    3|(IO) Mark: 1234/1234  (DF) SmMorse : 333
  848. 20    4|(KL) Spc : 1234/1234  (--) Offset  : 333
  849. 21  5|(ZX) Nois: 1234
  850. 22    7|* toggles freq autoadjust. (T) Tuneout *
  851. 23    8|| and | focus.  ESC exits.  SHIFT+| & |
  852. 24  9|SPACE toggles audio (xxx).  move tuneout
  853. ----------------------------------------------- tone display line
  854. */
  855.  
  856. void updateDisplay(void)
  857. {
  858.     gotoxy(38,17);    cprintf("(QW) Freq: %-5u %c  ---- Overruns: %-5d", sbSampleRate, ((autorate)?('*'):(' ')), sbOver);
  859.     gotoxy(38,18);    cprintf("(AS) Skip: %-3d        (ER) Smooth  : %-3d  ", waitfactor, cntFFT);
  860.     gotoxy(38,19);    cprintf("(IO) Mark: %-4u/%-4u  (DF) SmoMorse: %-3d", avgMark, useMark, cntBuf);
  861.     gotoxy(38,20);    cprintf("(KL) Spc : %-4u/%-4u  (%c%c) Offset  : %3d  ", avgSpace, useSpace, 0x1B, 0x1A, offset);
  862.     gotoxy(38,21);    cprintf("(ZX) Nois: %-4u", thrNoise);
  863.     gotoxy(38,22);    cprintf("* toggles freq autoadjust. (T) Tuneout %c", ((tuneout)?('*'):(' ')));
  864.     gotoxy(38,23);    cprintf("%c and %c focus.  ESC exits.  SHIFT+%c & %c", 0x18, 0x19, 0x18, 0x19);
  865. //    gotoxy(38,24);    cprintf("SPACE toggles audio (%s  move tuneout", ((noise)?("on). "):("off).")));
  866. }
  867.  
  868. void clearMorse(void)
  869. {
  870.     totMark = avgMark = 0;
  871.     totSpace = avgSpace = 0;
  872.     notMarkReset = FALSE;
  873.     notSpaceReset = FALSE;
  874.     memset(bufMark, 0, sizeof(bufMark));
  875.     memset(bufSpace, 0, sizeof(bufSpace));
  876.     memset(maxlevel, 0, sizeof(maxlevel));
  877.     updateDisplay();
  878. }
  879.  
  880.  
  881. void handlekey(char *done)
  882. {
  883.     int bk,l,c,col;
  884.     updateCursors(FALSE);
  885.     bk = bioskey(0);
  886.     switch (bk)
  887.     {
  888.         case SUPKEY:
  889.             if (out>1) out--;
  890.             break;
  891.         case SDOWNKEY:
  892.             if (out<(N-3)) out++;
  893.             break;
  894.         case UPKEY:
  895.             if (freq>2) freq--;
  896.             break;
  897.         case DOWNKEY:
  898.             if (freq<(N-4)) freq++;
  899.             break;
  900.         case LEFTKEY:
  901.             if (offset>0) offset--;
  902.             break;
  903.         case RIGHTKEY:
  904.             if (offset<32) offset++;
  905.             break;
  906.         case ESCKEY:
  907.             *done=TRUE;
  908.             break;
  909.         default:
  910.             switch (bk&0xFF)
  911.             {
  912.                 case 'q':
  913.                 case 'Q':
  914.                     if (sbSampleRate>SAMPLEMIN)
  915.                     {
  916.                         sbSampleRate -= SAMPLEINC;
  917.                         updateDisplay();
  918.                     }
  919.                     break;
  920.                 case 'w':
  921.                 case 'W':
  922.                     if (sbSampleRate<SAMPLEMAX)
  923.                     {
  924.                         sbSampleRate += SAMPLEINC;
  925.                         updateDisplay();
  926.                     }
  927.                     break;
  928.                 case 'e':
  929.                 case 'E':
  930.                     if (cntFFT>1)
  931.                     {
  932.                         cntFFT--;
  933.                         updateDisplay();
  934.                         clearGraph();
  935.                     }
  936.                     break;
  937.                 case 'r':
  938.                 case 'R':
  939.                     if (cntFFT<MAXFFT)
  940.                     {
  941.                         cntFFT++;
  942.                         updateDisplay();
  943.                         clearGraph();
  944.                     }
  945.                     break;
  946.                 case 'a':
  947.                 case 'A':
  948.                     if (waitfactor>1)
  949.                     {
  950.                         waitfactor--;
  951.                         updateDisplay();
  952.                     }
  953.                     break;
  954.                 case 's':
  955.                 case 'S':
  956.                     if (waitfactor<128)
  957.                     {
  958.                         waitfactor++;
  959.                         updateDisplay();
  960.                     }
  961.                     break;
  962.                 case ' ':
  963.                     noise = !noise;
  964.                     if (!noise) nosound();
  965.                     updateDisplay();
  966.                     break;
  967.                 case '8':
  968.                 case '*':
  969.                     autorate = !autorate;
  970.                     updateDisplay();
  971.                     break;
  972.                 case 'd':
  973.                 case 'D':
  974.                     if (cntBuf>MINBUF)
  975.                     {
  976.                         cntBuf--;
  977.                         clearMorse();
  978.                     }
  979.                     break;
  980.                 case 'f':
  981.                 case 'F':
  982.                     if (cntBuf<MAXBUF)
  983.                     {
  984.                         cntBuf++;
  985.                         clearMorse();
  986.                     }
  987.                     break;
  988.                 case 'i':
  989.                     if (useMark>1)
  990.                     {
  991.                         useMark--;
  992.                         updateDisplay();
  993.                     }
  994.                     break;
  995.                 case 'I':
  996.                     if (useMark>100)
  997.                     {
  998.                         useMark-=100;
  999.                         updateDisplay();
  1000.                     }
  1001.                     break;
  1002.                 case 'o':
  1003.                     if (useMark<9999)
  1004.                     {
  1005.                         useMark++;
  1006.                         updateDisplay();
  1007.                     }
  1008.                     break;
  1009.                 case 'O':
  1010.                     if (useMark<9899)
  1011.                     {
  1012.                         useMark+=100;
  1013.                         updateDisplay();
  1014.                     }
  1015.                     break;
  1016.                 case 'k':
  1017.                     if (useSpace>1)
  1018.                     {
  1019.                         useSpace--;
  1020.                         updateDisplay();
  1021.                     }
  1022.                     break;
  1023.                 case 'K':
  1024.                     if (useSpace>100)
  1025.                     {
  1026.                         useSpace -= 100;
  1027.                         updateDisplay();
  1028.                     }
  1029.                     break;
  1030.                 case 'l':
  1031.                     if (useSpace<9999)
  1032.                     {
  1033.                         useSpace++;
  1034.                         updateDisplay();
  1035.                     }
  1036.                     break;
  1037.                 case 'L':
  1038.                     if (useSpace<9899)
  1039.                     {
  1040.                         useSpace+=100;
  1041.                         updateDisplay();
  1042.                     }
  1043.                     break;
  1044.                 case 'z':
  1045.                     if (thrNoise>1)
  1046.                     {
  1047.                         thrNoise--;
  1048.                         updateDisplay();
  1049.                     }
  1050.                     break;
  1051.                 case 'Z':
  1052.                     if (thrNoise>100)
  1053.                     {
  1054.                         thrNoise-=100;
  1055.                         updateDisplay();
  1056.                     }
  1057.                     break;
  1058.                 case 'x':
  1059.                     if (thrNoise<9999)
  1060.                     {
  1061.                         thrNoise++;
  1062.                         updateDisplay();
  1063.                     }
  1064.                     break;
  1065.                 case 'X':
  1066.                     if (thrNoise<9899)
  1067.                     {
  1068.                         thrNoise+=100;
  1069.                         updateDisplay();
  1070.                     }
  1071.                     break;
  1072.                 case 't':
  1073.                 case 'T':
  1074.                     tuneout = !tuneout;
  1075.                     updateDisplay();
  1076.                     break;
  1077.             }
  1078.     }
  1079.     updateCursors(TRUE);
  1080. }
  1081.  
  1082. /*------------------------------- Initialize ---------------------------------*/
  1083.  
  1084. //
  1085. //    Initialize the screen, data and hardware.
  1086. //
  1087. void begin(void)
  1088. {
  1089.     static int l,line,xx,yy;
  1090.                                         // Set up the Sound Blaster, and
  1091.                                         // display (although briefly) some
  1092.                                         // interesting information about it.
  1093.     sbOpen();
  1094.  
  1095.     printf("\nSetStat: %d", sbSetStatWord(&sbStat));
  1096.     printf("\nRecSrc : %d", sbSetRecSrc(3));            // line in
  1097.     printf("\nRecMode: %d", sbSetRecMode(0));            // mono recording
  1098.     printf("\nSpkOnOf: %d", sbSpeaker(1));                // speaker goes on
  1099.                                         // Clear the screen.  Set up static
  1100.                                         // text and color regions.
  1101.     clrscr();
  1102.     clearGraph();
  1103.                                         // Display box horizontal lines
  1104.     for (xx=1; xx<35; xx++)
  1105.     {
  1106.         pokeb(VIDSEG,(xx<<1)+(160*BOXTOP),0xC4);
  1107.         pokeb(VIDSEG,(xx<<1)+(160*BOXDIV),0xC4);
  1108.         pokeb(VIDSEG,(xx<<1)+(160*BOXBOT),0xC4);
  1109.     }
  1110.                                         // Display box vertical lines
  1111.     for (yy=1; yy<23; yy++)
  1112.     {
  1113.         pokeb(VIDSEG,(160*yy)+(BOXLEF<<1),0xB3);
  1114.         pokeb(VIDSEG,(160*yy)+(BOXRIG<<1),0xB3);
  1115.     }
  1116.                                         // Display box & divide corners
  1117.     pokeb(VIDSEG,(160*BOXTOP)+(BOXLEF<<1),0xDA);
  1118.     pokeb(VIDSEG,(160*BOXTOP)+(BOXRIG<<1),0xBF);
  1119.     pokeb(VIDSEG,(160*BOXDIV)+(BOXLEF<<1),0xC3);
  1120.     pokeb(VIDSEG,(160*BOXDIV)+(BOXRIG<<1),0xB4);
  1121.     pokeb(VIDSEG,(160*BOXBOT)+(BOXLEF<<1),0xC0);
  1122.     pokeb(VIDSEG,(160*BOXBOT)+(BOXRIG<<1),0xD9);
  1123.  
  1124.     updateCursors(TRUE);
  1125.                                         // Display the left base for the
  1126.                                         // on-screen audio spectrum analyzer.
  1127.     for (l=0,line=0; l<(N-1); l++,line+=160)
  1128.     {
  1129.         pokeb(VIDSEG,80+line,'≡');
  1130.     }
  1131.                                         // Allocate room for the input and
  1132.                                         // analysis sample buffers.  Make
  1133.                                         // sure they start at offset 0000.
  1134.                                         // Allocate 64 extra bytes, 16 for
  1135.                                         // zero-offset, and 48 for breathing
  1136.                                         // space.
  1137.     bufone = (char far *)farmalloc(SAMPLELEN+0x40);
  1138.     buftwo = (char far *)farmalloc(SAMPLELEN+0x40);
  1139.     if ((bufone==NULL) || (buftwo==NULL))
  1140.     {
  1141.         puts("Woops!  No room for buffers!");
  1142.         exit(1);
  1143.     }
  1144.     bufone = MK_FP(FP_SEG(bufone)+1,0);
  1145.     buftwo = MK_FP(FP_SEG(buftwo)+1,0);
  1146.                                         // Display initial control panel.
  1147.     updateDisplay();
  1148. }
  1149.  
  1150. /////////////////////////////////////////////////////////////////////////////
  1151. //
  1152. //    Mainline function.
  1153. //
  1154. #pragma argsused
  1155. int main(int argc, char **argv)
  1156. {
  1157.     int rv, l, line;
  1158.     UINT sampBytes=0;
  1159.  
  1160.     begin();
  1161.  
  1162.     while (!done)
  1163.     {
  1164.                                         // swap input/dsp buffers for DMA
  1165.         if (bufin==bufone)
  1166.         {
  1167.             bufin = buftwo;
  1168.             bufdsp = bufone;
  1169.         }
  1170.         else
  1171.         {
  1172.             bufin = bufone;
  1173.             bufdsp = buftwo;
  1174.         }
  1175.                                         // record to bufin
  1176.         if (sbInVoc(sbSampleRate, bufin, SAMPLELEN))
  1177.         {
  1178.             puts("Error recording.");
  1179.             goto quickdone;
  1180.         }
  1181.         // Process the DSP buffer from last read.  Skip the 16-byte block
  1182.         // header that CT-VOICE.DRV uses to describe the block
  1183.         dspcount = 16;
  1184.         bufdsp += 16;
  1185.         while (dspcount<SAMPLELEN)
  1186.         {
  1187.             dspcount += FFT(bufdsp);
  1188.             FFT2tone();
  1189.         }
  1190.  
  1191.         sampBytes += SAMPLELEN;
  1192.                                         // Sample bytes tested >= 1 sec worth
  1193.         if (sampBytes >= sbSampleRate)
  1194.         {
  1195.             sampBytes = 0;
  1196.             for (l=0,line=0; l<(N-1); l++,line+=160)
  1197.             {
  1198.                 if (maxlevel[l]>1)
  1199.                 {
  1200.                     pokeb(VIDSEG,80+line+((maxlevel[l])*2),0xFA);
  1201.                     maxlevel[l]--;
  1202.                     pokeb(VIDSEG,80+line+((maxlevel[l])*2),0x07);
  1203.                 }
  1204.                 else
  1205.                 {
  1206.                     pokeb(VIDSEG,82+line,0xFA);
  1207.                 }
  1208.             }
  1209.         }
  1210.                                         // handle keypresses
  1211.         if (kbhit())
  1212.         {
  1213.             handlekey(&done);
  1214.             if (done) goto quickdone;
  1215.         }
  1216.                                         // eat time until record finishes
  1217.         if (sbStat)
  1218.         {
  1219.                                         // Update display only if is time
  1220.             updateDisplay();
  1221.  
  1222.             while (sbStat)
  1223.             {
  1224.                 if (kbhit())
  1225.                 {
  1226.                     handlekey(&done);
  1227.                     if (done) goto quickdone;
  1228.                 }
  1229.                 idlePutC(0xF0);
  1230.             }
  1231.         }
  1232.         else
  1233.         {
  1234.             if (autorate)
  1235.             {
  1236.                 if (sbSampleRate>SAMPLEMIN)
  1237.                 {
  1238.                     sbSampleRate-=SAMPLEINC;
  1239.                 }
  1240.             }
  1241.             if (sbOver<0xFFFF) sbOver++;
  1242.             updateDisplay();
  1243.             idlePutC(0xEC);
  1244.         }
  1245.         idlePutC(0x20);
  1246.     }
  1247.     while (kbhit()) getch();
  1248. quickdone:
  1249.     gotoxy(1,24);
  1250.     nosound();
  1251.     sbSpeaker(0);    // turn speaker back on
  1252.     sbClose();
  1253.     return(0);
  1254. }
  1255.